// 抽象工厂模式
// 通过对类的工厂抽象，用于对产品类簇的创建，而不负责创建某一类产品的实例。

// js 抽象类
// js中abstract是保留字，不能使用其创建抽象类
// js可以通过抛异常表示抽象方法 
var Car = function() {};
Car.prototype = {
  getPrice() {
    // 调用抽象函数，提示错误
    return new Error('抽象方法不能调用');
  },
  getSpeed() {
    return new Error('抽象方法不能调用');
  }
};


// 抽象工厂方法:用于创建工厂;实际是一个寄生式继承的过程
var VehicleFactory = function(subType, superType) {
  // 判断抽象工厂中是否有该抽象类
  if(typeof VehicleFactory[superType] === 'function') {
    // 缓存类
    function F() {}
    // 继承父类属性和方法
    // 注意：这里与寄生式继承(复制一份prototype)不同，而是创建了一个父类对象作为原型
    // 因为这里不仅仅要继承父类的原型，还要继承父类属性
    F.prototype = new VehicleFactory[superType]();
    // 将子类constructor指向子类
    subType.constructor = subType;
    subType.prototype = new F();
  } else {
    throw new Error('未创建该抽象类');
  }
}

// 抽象类
// 小汽车抽象类
VehicleFactory.Car = function() {
  this.type = 'car';
}
VehicleFactory.Car.prototype = {
  getPrice() {
    return new Error('抽象方法不能调用');
  },
  getSpeed() {
    return new Error('抽象方法不能调用');
  }
}
// 公交车抽象类
VehicleFactory.Bus = function() {
  this.type = 'bus';
}
VehicleFactory.Bus.prototype = {
  getPrice() {
    return new Error('抽象方法不能调用');
  },
  getPassengerNum() {
    return new Error('抽象方法不能调用');
  }
}
// 货车抽象类
VehicleFactory.Trunk = function() {
  this.type = 'trunk';
}
VehicleFactory.Trunk.prototype = {
  getPrice() {
    return new Error('抽象方法不能调用');
  },
  getTrainLoad() {
    return new Error('抽象方法不能调用');
  }
}

// 实现类
// 1.宝马汽车子类
var BMW = function(price, speed) {
  this.price = price;
  this.speed = speed;
}
// 抽象工厂实现对Car抽象类的继承
VehicleFactory(BMW, 'Car');
BMW.prototype.getPrice = function() {
  return this.price;
}
BMW.prototype.getSpeed = function() {
  return this.speed;
}
// 2.宇通汽车子类
var TUTONG = function(price, passenger) {
  this.price = price;
  this.passenger = passenger;
}
// 抽象工厂实现对Bus抽象类的继承
VehicleFactory(TUTONG, 'Bus');
TUTONG.prototype.getPrice = function() {
  return this.price;
}
TUTONG.prototype.getPassengerNum = function() {
  return this.passenger;
}
// 3.奔驰汽车子类
var BenzTrunk = function(price, trainLoad) {
  this.price = price;
  this.trainLoad = trainLoad;
}
VehicleFactory(BenzTrunk, 'Trunk');
BenzTrunk.prototype.getPrice = function() {
  return this.price;
}
BenzTrunk.prototype.getTrainLoad = function() {
  return this.trainLoad;
}

// 测试用例
var trunk = new BenzTrunk(1000000, 1000);
console.log(trunk.getPrice()); // 1000000
console.log(trunk.type);       // trunk
